#include "NapiUtils.h"

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN  0x0
#define LOG_TAG     "NapiUtils"
#include <hilog/log.h>


napi_value NapiUtils::javascriptClassConstructor(napi_env env, napi_callback_info info) {
    napi_value thisArg = nullptr;
    void *data = nullptr;
    NAPI_RETURN_IF_NOT_OK(nullptr, napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data));
    napi_value global = nullptr;
    NAPI_RETURN_IF_NOT_OK(nullptr, napi_get_global(env, &global));
    return thisArg;
}


NapiUtils::ThreadSafeFunction::ThreadSafeFunction(napi_threadsafe_function tsfn) : tsfn(tsfn) {
}

NapiUtils::ThreadSafeFunction::~ThreadSafeFunction() {
    if (tsfn) {
        (void) [&] { NAPI_RETURN_IF_NOT_OK(napiCallResult, napi_release_threadsafe_function(tsfn, napi_tsfn_abort)); return napi_ok; }();
    }
    tsfn = nullptr;
}

napi_status NapiUtils::ThreadSafeFunction::operator()(void *data) {
    if (!tsfn) {
        return napi_closing;
    }
    if (auto napiCallResult = napi_call_threadsafe_function(tsfn, data, napi_tsfn_blocking); napiCallResult != napi_ok) {
        OH_LOG_WARN(LOG_APP, "Failed to call thread-safe function %{public}d", napiCallResult);
        tsfn = nullptr;
        return napiCallResult;
    }
    return napi_ok;
}

NapiUtils::ThreadSafeFunction::Arguments::Arguments(std::initializer_list<NapiValueType> l) : data(l) {
}

NapiUtils::ThreadSafeFunction::Arguments::~Arguments() = default;

std::vector<napi_value> NapiUtils::ThreadSafeFunction::Arguments::toNapiValues(napi_env env) const {
    std::vector<napi_value> values;
    for (const auto &d : data) {
        values.push_back(std::visit([&] (auto &&d) {
            return toNapiValue(env, d);
        }, d));
    }
    return values;
}


void NapiUtils::callJsCb(napi_env env, napi_value jsCallback, void *context, void *data) {
    using Arg = ThreadSafeFunction::Arguments;
    /* data will be automatically deleted once out of this callback */
    const std::unique_ptr<Arg> args{reinterpret_cast<Arg*>(data)};
    auto jsArgs = args ? args->toNapiValues(env) : std::vector<napi_value>();
    (void) [&] { NAPI_RETURN_IF_NOT_OK(napiCallResult, napi_call_function(env, nullptr, jsCallback,
        jsArgs.size(), jsArgs.data(), nullptr)); return napi_ok; }();
}
